package com.moma.chatting.core;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.moma.chatting.exception.*;
// Conversation should be responsible for thread safe.
public class Conversation {

	private final static Logger logger;
	static {
		logger = LoggerFactory.getLogger(Conversation.class);
	}
	private Conversation(){
//		this.logger = LoggerFactory.getLogger(Conversation.class);
//		this.conversationId = 1; // TODO: Jimmy Mo - Hard coded
		this.conversationId = Utility.getUniqueInteger();
		this.group =  new DynamicGroup();
		UUID randomGroupId = UUID.randomUUID();
		this.group.setGroupId(randomGroupId);
		this.group.setName("tempGroup_" + randomGroupId.toString());
		this.historyMessages = new ArrayList<Message>();
		this.userMessageDict = new HashMap<String, BlockingQueue<Message>>();
	}
	
	private ReentrantLock modifyUserlock = new ReentrantLock();
	private ReentrantLock recyclerFlagLock = new ReentrantLock();
	private boolean recyclerFlag = false;
	public void setRecyclerFlag(boolean value){
		recyclerFlagLock.lock();
		try{
			this.recyclerFlag = value;
		}
		finally{
			recyclerFlagLock.unlock();
		}
	}
	public boolean getRecyclerFlag(){
		recyclerFlagLock.lock();
		try
		{
			return this.recyclerFlag;
		}
		finally{
			recyclerFlagLock.unlock();
		}
	}
	
	private int conversationId;
	public int getConversationId(){
		return this.conversationId;
	}
	@Deprecated
	public void setConversationId(int value){
		this.conversationId = value;
	}
	
	private List<Message> historyMessages;
	public void setHistoryMessages(List<Message> value){
		this.historyMessages = value;
	}
	public List<Message> getHistoryMessages(){
		return this.historyMessages;
	}
	
	//<UserUniqueName, BlockingQueue<Message>>
	private HashMap<String, BlockingQueue<Message>> userMessageDict;
	
	private DynamicGroup group;
	public DynamicGroup getGroup(){
		return this.group;
	}
	
	public static Conversation createConversation(User[] users){
		logger.trace("Enter create conversation method...");
		Conversation conv = new Conversation();
		logger.info(String.format("Conversation %d is created...", conv.getConversationId()));
		
		for (User user : users){
			try {
				conv.addUser(user);
				logger.info(String.format("User %s is added to conversation %d...", user.getUniqueName(), conv.getConversationId()));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				logger.error("Create conversation throws exception..." + e.getStackTrace());
			}
		}
//		int conversationId = conv.getConversationId();
		logger.trace("Leaving create conversation method...");
		return conv;
//		return conversationId;
	}
	public void addToHistory(Message msg){
		synchronized (this.historyMessages){
			this.historyMessages.add(msg);
		}
	}
	public void dispatchMessage(String userUniqueName, Message msg) throws InterruptedException, UserExpiredException{
		User user = this.group.getUser(userUniqueName);
		if (!user.getIsOnline()){
			logger.error(String.format("User %s is no longer active...", userUniqueName));
			throw new UserExpiredException(userUniqueName + " expired!");
		}
		BlockingQueue<Message> userMessageQueue = this.userMessageDict.get(userUniqueName);
//		System.out.println(String.format("dispatch one msg...%s", msg.getContent()));
		logger.info(String.format("dispatch message %s...", msg.getContent()));
		userMessageQueue.put(msg);
	}
	
	public Message[] retrieveMessage(String userUniqueName) throws InterruptedException, UserExpiredException{
		User user = this.group.getUser(userUniqueName);
		if (!user.getIsOnline()){
			logger.error(String.format("User %s is no longer active...", userUniqueName));
			throw new UserExpiredException(userUniqueName + " expired!");
		}
		BlockingQueue<Message> userMessageQueue = this.userMessageDict.get(userUniqueName);
		ArrayList<Message> msgList = new ArrayList<Message>();
		logger.info("retrieving message...");
		while (userMessageQueue.size() > 0){
			msgList.add(userMessageQueue.take());
		}
		Message[] msgArray = new Message[msgList.size()];
		msgList.toArray(msgArray);
		logger.info(String.format("retrieved message %s...", msgArray.length));
		return msgArray;
	}
	
	public void addUser(User user) throws DuplicationException, NotExistException{
		modifyUserlock.lock();
		try{
			this.group.addUser(user);
			user.reportOnline();
			BlockingQueue<Message> msgQueue = new ArrayBlockingQueue<Message>(1000);
			this.userMessageDict.put(user.getUniqueName(), msgQueue);
		}
		finally{
			modifyUserlock.unlock();
		}
	}
	
	public void removeUser(String userUniqueName) throws NotExistException{
		modifyUserlock.lock();
		try{
			this.group.removeUser(userUniqueName);
			this.userMessageDict.remove(userUniqueName);
		}
		finally{
			modifyUserlock.unlock();
		}
	}
	
//	public synchronized void reportUserAlive(String userUniqueName) throws NotExistException{
////		User user = this.group.getUserMap().get(userUniqueName);
//		User user = this.group.getUser(userUniqueName);
//		user.reportOnline();
////		this.group.reportAlive(user.getUniqueName());
//	}
}
